package cn.minimelon.api.service.module.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.thread.GlobalThreadPool;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.minimelon.api.constant.ModuleConstants;
import cn.minimelon.api.domain.common.LoginUser;
import cn.minimelon.api.domain.common.ProgressInfo;
import cn.minimelon.api.domain.enums.AuditFlagEnum;
import cn.minimelon.api.domain.enums.SwTypeEnum;
import cn.minimelon.api.domain.module.dto.ModelTaskData;
import cn.minimelon.api.domain.module.po.DataModel;
import cn.minimelon.api.domain.module.vo.DataModelVO;
import cn.minimelon.api.domain.module.vo.DataPropVO;
import cn.minimelon.api.domain.module.vo.ModelFullVO;
import cn.minimelon.api.exception.ServiceException;
import cn.minimelon.api.mapper.module.DataModelMapper;
import cn.minimelon.api.mapper.module.DataPropMapper;
import cn.minimelon.api.mapper.module.LifeCycleMapper;
import cn.minimelon.api.service.module.IDataModelService;
import cn.minimelon.api.service.module.thread.ModelTaskRunnable;
import cn.minimelon.api.service.module.utils.ModelUtil;
import cn.minimelon.api.service.module.utils.PropUtils;
import cn.minimelon.api.utils.MelonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.solon.annotation.Db;
import org.noear.redisx.RedisClient;
import org.noear.redisx.plus.RedisBus;
import org.noear.snack.ONode;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import org.noear.solon.cache.jedis.RedisCacheService;
import org.noear.solon.data.annotation.Transaction;

import java.util.Date;
import java.util.List;

@Slf4j
@Component
public class DataModelServiceImpl implements IDataModelService {
    @Db
    private DataModelMapper modelMapper;

    @Db
    private DataPropMapper propMapper;

    @Db
    private LifeCycleMapper lifeCycleMapper;

    @Inject
    private RedisCacheService cacheService;

    @Inject
    private RedisClient redisClient;

    @Override
    public List<DataModelVO> selectList(DataModelVO dataModel) {
        return modelMapper.selectEntityList(dataModel);
    }

    @Override
    public DataModelVO selectById(String uid) {
        DataModel dataModel = new DataModel();
        dataModel.setUid(uid);
        DataModelVO info = modelMapper.selectEntity(dataModel);
        ModelUtil.buildJsonInfo(info);
        return info;
    }

    @Override
    public String checkCodeUnique(DataModelVO dataModel) {
        String uid = ObjUtil.isNull(dataModel.getUid()) ? "" : dataModel.getUid();
        DataModel info = modelMapper.selectByCode(dataModel.getCode());
        if (ObjUtil.isNotNull(info) && !uid.equals(info.getUid())) {
            return SwTypeEnum.YES.val();
        }
        return SwTypeEnum.NO.val();
    }

    @Transaction
    @Override
    public int insert(DataModelVO dataModel) {
        // JSON属性处理
        if (MapUtil.isNotEmpty(dataModel.getExt())) {
            dataModel.setExtJson(ONode.serialize(dataModel.getExt()));
        }
        if (MapUtil.isNotEmpty(dataModel.getSwt())) {
            dataModel.setSwtJson(ONode.serialize(dataModel.getSwt()));
        }
        int count = modelMapper.insert(dataModel);
        if (count > 0) {
            String classifySw = dataModel.getClassifySw();
            List<DataPropVO> tplPropList = propMapper.selectTplPropList(dataModel);
            // 插入默认属性，按类型区分
            for (DataPropVO dataProp : tplPropList) {
                dataProp.setUid(MelonUtils.snowId("P"));
                dataProp.setModelCode(dataModel.getCode());
                dataProp.setCreateTime(new Date());
                dataProp.setUpdateTime(new Date());
                dataProp.setCreateBy(dataModel.getCreateBy());
                dataProp.setUpdateBy(dataModel.getUpdateBy());
                dataProp.setVipLevel(dataModel.getVipLevel());
                if (dataProp.getColNum() > 80) {
                    dataProp.setColNum(dataProp.getColNum() + 900);
                }
                // 分类编码
                if (StrUtil.equals(dataProp.getCode(), ModuleConstants.PROP_CATE_CODE)) {
                    if (StrUtil.equals(classifySw, SwTypeEnum.YES.val())) {
                        dataProp.setLinkCode(dataModel.getLinkCode());
                    } else {
                        dataProp.setEditFlag(dataProp.getEditFlag() + 4); // 停用
                    }
                }
                propMapper.insert(dataProp);
            }
        }
        return count;
    }

    @Override
    public int update(DataModelVO dataModel) {
        // JSON属性处理
        if (MapUtil.isNotEmpty(dataModel.getExt())) {
            dataModel.setExtJson(ONode.serialize(dataModel.getExt()));
        }
        if (MapUtil.isNotEmpty(dataModel.getSwt())) {
            dataModel.setSwtJson(ONode.serialize(dataModel.getSwt()));
        }
        return modelMapper.updateById(dataModel);
    }

    @Transaction
    @Override
    public int deleteById(String uid) {
        // 校验状态，通知删除数据
        DataModelVO model = selectById(uid);
        propMapper.deleteByModelCode(model.getCode());
        return modelMapper.deleteById(uid);
    }

    @Override
    public ModelFullVO selectFullInfo(String code) {
        ModelFullVO info = modelMapper.selectByCode(code);
        ModelUtil.buildJsonInfo(info);
        List<DataPropVO> propList = propMapper.selectListByModelCode(code);
        for (DataPropVO prop : propList) {
            PropUtils.decodeJsonInfo(prop);
        }
        info.setPropList(propList);
        return info;
    }

    @Override
    public String submit(ModelTaskData taskData) {
        List<DataModelVO> list = taskData.getModelList();
        if (CollUtil.isEmpty(list)) {
            throw new ServiceException("请选择需要提交的模型");
        }
        String progressKey = taskData.cacheKey();
        ProgressInfo progressInfo = cacheService.get(progressKey, ProgressInfo.class);
        if (progressInfo == null) {
            GlobalThreadPool.submit(new ModelTaskRunnable(taskData, this::execSubmitOne));
        }
        return progressKey;
    }

    @Transaction
    @Override
    public String execSubmitOne(ModelTaskData taskData) {
        DataModelVO model = taskData.getDataModel();
        // 1. 校验锁与加锁，防止并发提交
        String key = "lock:module:model:submit:" + model.getUid();
        String lock = cacheService.get(key, String.class);
        if (StrUtil.isNotEmpty(lock)) {
            throw new ServiceException(StrUtil.format("模型已锁定", model.getCode()));
        } else {
            cacheService.store(key, StrUtil.DASHED, 300);
        }
        // 2. 校验属性快捷搜索数量
        List<DataPropVO> propList = propMapper.selectListByModelCode(model.getCode());
        ModelUtil.getInstance().validIdxCol(propList);
        // 3. 分配索引列
        List<DataPropVO> updList = ModelUtil.getInstance().distributeHideCol(propList);
        updList.forEach(it -> propMapper.updateColById(it));
        // 4. 更新状态为提交，启用工作流
        DataModel update = new DataModel();
        update.setUid(model.getUid());
        update.setAuditFlag(AuditFlagEnum.AUDIT.getVal());
        update.setRejectFlag(SwTypeEnum.NO.code());
        update.setUpdateBy(taskData.getUserId());
        update.setUpdateTime(new Date());
        modelMapper.updateById(update);
        // 解除锁定
        cacheService.remove(key);
        return model.getUid();
    }

    @Override
    public String reject(ModelTaskData taskData) {
        LoginUser user = taskData.getUser();
        List<DataModelVO> list = taskData.getModelList();
        if (CollUtil.isEmpty(list)) {
            throw new ServiceException("请选择需要拒绝的模型");
        }
        String progressKey = "progress:module:model:reject:" + user.getUserId();
        ProgressInfo progressInfo = cacheService.get(progressKey, ProgressInfo.class);
        if (progressInfo == null) {
            GlobalThreadPool.submit(new ModelTaskRunnable(taskData, this::execRejectOne));
        }
        return progressKey;
    }

    @Override
    public String approve(ModelTaskData taskData) {
        LoginUser user = taskData.getUser();
        List<DataModelVO> list = taskData.getModelList();
        if (CollUtil.isEmpty(list)) {
            throw new ServiceException("请选择需要通过的模型");
        }
        String progressKey = "progress:module:model:approve:" + user.getUserId();
        ProgressInfo progressInfo = cacheService.get(progressKey, ProgressInfo.class);
        if (progressInfo == null) {
            GlobalThreadPool.submit(new ModelTaskRunnable(taskData, this::execApproveOne));
        }
        return progressKey;
    }

    @Transaction
    @Override
    public String execRejectOne(ModelTaskData taskData) {
        DataModelVO model = taskData.getDataModel();
        // 1. 校验锁与加锁，防止并发提交
        String key = "lock:module:model:submit:" + model.getUid();
        String lock = cacheService.get(key, String.class);
        if (StrUtil.isNotEmpty(lock)) {
            throw new ServiceException(StrUtil.format("模型已锁定", model.getCode()));
        } else {
            cacheService.store(key, StrUtil.DASHED, 300);
        }
        // 2. 更新状态为提交，决绝状态为已拒绝
        DataModel update = new DataModel();
        update.setUid(model.getUid());
        update.setAuditFlag(AuditFlagEnum.SUBMIT.getVal());
        update.setRejectFlag(SwTypeEnum.YES.code());
        update.setUpdateBy(taskData.getUserId());
        update.setUpdateTime(new Date());
        modelMapper.updateById(update);
        // 解除锁定
        cacheService.remove(key);
        return model.getUid();
    }

    @Transaction
    @Override
    public String execApproveOne(ModelTaskData taskData) {
        DataModelVO model = taskData.getDataModel();
        // 1. 校验锁与加锁，防止并发提交
        String key = "lock:module:model:submit:" + model.getUid();
        String lock = cacheService.get(key, String.class);
        if (StrUtil.isNotEmpty(lock)) {
            throw new ServiceException(StrUtil.format("模型已锁定", model.getCode()));
        } else {
            cacheService.store(key, StrUtil.DASHED, 300);
        }
        // 2. 更新状态为提交，决绝状态为已拒绝
        DataModel update = new DataModel();
        update.setUid(model.getUid());
        update.setAuditFlag(AuditFlagEnum.DEPLOY.getVal());
        update.setRejectFlag(SwTypeEnum.NO.code());
        update.setUpdateBy(taskData.getUserId());
        update.setUpdateTime(new Date());
        modelMapper.updateById(update);
        // 解除锁定
        cacheService.remove(key);
        return model.getUid();
    }

    @Override
    public String deploy(ModelTaskData taskData) {
        if (CollUtil.isEmpty(taskData.getModelList())) {
            throw new ServiceException("请选择需要发布的模型");
        }
        String progressKey = "progress:module:model:deploy:" + taskData.getUser().getUserId();
        ProgressInfo progressInfo = cacheService.get(progressKey, ProgressInfo.class);
        if (progressInfo == null) {
            GlobalThreadPool.submit(new ModelTaskRunnable(taskData, this::execDeployOne));
        }
        return progressKey;
    }

    @Transaction
    @Override
    public String execDeployOne(ModelTaskData taskData) {
        DataModelVO uim = taskData.getDataModel();
        DataModel model = modelMapper.selectById(uim.getUid());
        model.setUpdateBy(taskData.getUserId());
        // 1. 校验锁与加锁，防止并发提交
        String key = "lock:module:model:deploy:" + model.getUid();
        String lock = cacheService.get(key, String.class);
        if (StrUtil.isNotEmpty(lock)) {
            throw new ServiceException(StrUtil.format("模型已锁定", model.getCode()));
        } else {
            cacheService.store(key, StrUtil.DASHED, 300);
        }
        // 2. 更新状态为提交，决绝状态为已拒绝
        DataModel update = new DataModel();
        update.setUid(model.getUid());
        update.setAuditFlag(AuditFlagEnum.FINISH.getVal());
        update.setRejectFlag(SwTypeEnum.NO.code());
        update.setUpdateBy(taskData.getUserId());
        update.setUpdateTime(new Date());
        modelMapper.updateById(update);
        // 模型数据A~B
        int num = lifeCycleMapper.deleteDtbModelById(model);
        log.info("deleteDtbModelById > {}", num);
        num = lifeCycleMapper.insertDtbModel(model);
        log.info("insertDtbModel > {}", num);
        num = lifeCycleMapper.deleteDtbPropByCode(model);
        log.info("deleteDtbPropByCode > {}", num);
        num = lifeCycleMapper.insertDtbPropList(model);
        log.info("insertDtbPropList > {}", num);
        // 模型数据A~C
        num = lifeCycleMapper.insertDtcModel(model);
        log.info("insertDtcModel > {}", num);
        num = lifeCycleMapper.insertDtcPropList(model);
        log.info("insertDtcPropList > {}", num);
        // 通知数据模块处理：建表与重建索引等
        RedisBus bus = redisClient.getBus();
        bus.publish("model:deploy", ONode.stringify(model));
        // 解除锁定
        cacheService.remove(key);

        return model.getUid();
    }


}
